package com.softaria.spkiller.metadata.extractor;



import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;


import com.softaria.spkiller.metadata.AnnotationIdentifier;
import com.softaria.spkiller.metadata.AnnotationIdentifierImpl;
import com.softaria.spkiller.metadata.ClassIdentifier;
import com.softaria.spkiller.metadata.ClassIdentifierImpl;
import com.softaria.spkiller.metadata.ClassMetaData;
import com.softaria.spkiller.metadata.ClassMetaDataImpl;

public class AsmMetaDataExtractor implements MetaDataExtractor {

	private com.softaria.spkiller.metadata.extractor.InputStreamProvider isGetter;
	
	
	
	public AsmMetaDataExtractor(
			com.softaria.spkiller.metadata.extractor.InputStreamProvider isGetter) {
		super();
		this.isGetter = isGetter;
	}

	@Override
	public ClassMetaData getMetaData(InputStream inputStream) throws IOException {
		ClassReader classReader = new ClassReader(inputStream);
		
		final Collection<AnnotationIdentifier> annotations = new ArrayList<AnnotationIdentifier>();
		final ClassIdentifierImpl superClassIdentifier = new ClassIdentifierImpl();
		final ClassIdentifierImpl classIdentifier = new ClassIdentifierImpl();
		final Collection<ClassIdentifier> interfaces = new ArrayList<ClassIdentifier>();
		
		classReader.accept(new ClassVisitor() {
			
			@Override
			public void visitSource(String source, String debug) {
			}
			
			@Override
			public void visitOuterClass(String owner, String name, String desc) {
			}
			
			@Override
			public MethodVisitor visitMethod(int access, String name, String desc,
					String signature, String[] exceptions) {
				return null;
			}
			
			@Override
			public void visitInnerClass(String name, String outerName,
					String innerName, int access) {
			}
			
			@Override
			public FieldVisitor visitField(int access, String name, String desc,
					String signature, Object value) {
				return null;
			}
			
			@Override
			public void visitEnd() {
			}
			
			@Override
			public void visitAttribute(Attribute attr) {
			}
			
			@Override
			public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
				final AnnotationIdentifierImpl annotation = new AnnotationIdentifierImpl(toClassName(desc));
				
				annotations.add(annotation);
				
				return new AnnotationVisitor() {
					
					@Override
					public void visitEnum(String name, String desc, String value) {
						
						
					}
					
					@Override
					public void visitEnd() {
						
						
					}
					
					@Override
					public AnnotationVisitor visitArray(String name) {
						
						return null;
					}
					
					@Override
					public AnnotationVisitor visitAnnotation(String name, String desc) {
						
						return null;
					}
					
					@Override
					public void visit(String name, Object value) {
						annotation.setValue(value);
						
					}
				};
			}
			
			@Override
			public void visit(int version, int access, String name, String signature,
					String superName, String[] interface_names) {
				
				superClassIdentifier.setClassName(toClassName(superName));
				classIdentifier.setClassName(toClassName(name));
				
				for(String inter_face:interface_names) {
					interfaces.add(new ClassIdentifierImpl(toClassName(inter_face)));
				}
				
			}
		}, 0);
		
		
		return  new ClassMetaDataImpl(annotations, interfaces, superClassIdentifier,classIdentifier);
		
		
	}

	protected String toClassName(String desc) {
		
		if(desc == null) return null;
		
		String ret =  desc.replaceAll("/", ".").trim();
		if(ret.startsWith("L")) {
			ret = ret.substring(1,ret.length());
		}
		if(ret.endsWith(";")) {
			ret = ret.substring(0,ret.length()-1);
		}
		return ret;
	}

	@Override
	public ClassMetaData getMetaData(ClassIdentifier classID)
			throws IOException {
		InputStream is = isGetter.getInputStream(classID);
		
		try {
			if(is ==null) return null;
			
			return getMetaData(is);
		}
		finally {
			if(is != null) {
				is.close();
			}
		}
		
		
	}

	
}
